#define PrynInternal
#define PrynImplementation
#include <pryn.h>

#define PrynCheckMonitor() \
	prynAssert (monitor, NullArgument); \
	prynCall (prynObjectState (&monitor->pObject, &state)); \

// Build a function for a monitor, ensuring it's non-zero before executing body, and assigns state.
#define PrynMonitorBody(BODY) \
	do { \
		PrynStartFunction (); \
		{ \
			PrynCheckMonitor (); \
			BODY \
		} \
		PrynEndFunction (); \
	} while (0)


PrynExport (PrynResult, prynMonitorState, (PrynMonitor *monitor, PrynState **output))
{
	if (output) *output = 0;
	PrynMonitorBody ({
		prynAssert (output, NullArgument);
		*output = state;
	});
}

PrynExport (PrynResult, prynMonitorTags, (PrynMonitor *monitor, PrynTagList **output))
{
	if (output) *output = 0;
	PrynMonitorBody ({
		prynAssert (output, NullArgument);
		*output = &monitor->pCommonObject.pTags;
	});
}

PrynExport (PrynResult, prynMonitorCreate, (PrynMonitor **output, const PrynObject *object, PrynChangeType changes, PrynMonitorNotifyFunction notify, void *data, PrynDeleteDataFunction destroyData))
{
	PrynStartFunction ();
	PrynMonitor *monitor;
	PrynMonitorList *list;

	if (output) *output = 0;
	prynCall (prynObjectState (object, &state));
	prynCall (prynObjectMonitors (object, &list));
	prynAssert (list, BadArgument);

	prynCall (prynAllocateClearedObject (state, &monitor));
	monitor->pCommonObject.pType = PrynObjectTypeMonitor;
	monitor->pObject = *object;
	monitor->pChanges = changes;
	monitor->pData = data;
	monitor->pDestroyData = destroyData;
	monitor->pNotifyFunction = notify;
	prynDListAppendP (*list, monitor);

	PrynEndFunction ();
}

PrynExport (PrynResult, prynMonitorListDestroy, (PrynMonitorList *list))
{
	if (!list) return PrynResultNullArgument;
	while (list->first)
		prynMonitorDestroy (list->first);
	return PrynResultSuccess;
}

PrynExport (PrynResult, prynMonitorDestroy, (PrynMonitor *monitor))
{
	PrynStartFunction ();
	PrynMonitorList *list;

	PrynCheckMonitor ();
	prynObjectMonitors (&monitor->pObject, &list);
	prynCommonObjectDestroy (&monitor->pCommonObject, state);
	if (monitor->pDestroyData) monitor->pDestroyData (monitor->pData);
	if (list) prynDListRemoveP (*list, monitor);
	prynFree (state, monitor);
	PrynEndFunction ();
}